import * as d3 from 'd3'
import * as d3Sankey from 'd3-sankey'
export class SankeyChart {
    divId = ''
    data = []
    chart = {}
    layout = 'sankeyJustify'
    color = d3.scaleOrdinal(d3.schemeCategory10)

    constructor (divId) {
      this.divId = divId
    }

    draw () {
      this.initChart()
      this.initLayout()
      this.drawSankey()
    }

    initChart () {
      this.chart.padding = {
        left: 10,
        right: 10,
        top: 10,
        bottom: 10
      }
      this.chart.divHeight = document.getElementById(this.divId).offsetHeight -
          this.chart.padding.top -
          this.chart.padding.bottom
      this.chart.divWidth = document.getElementById(this.divId).offsetWidth -
          this.chart.padding.left -
          this.chart.padding.right

      this.chart.svg = d3
        .select('#' + this.divId)
        .append('svg')
        .attr('width', this.chart.divWidth +
            this.chart.padding.left +
            this.chart.padding.right
        )
        .attr('height', this.chart.divHeight +
            this.chart.padding.top +
            this.chart.padding.bottom
        )
        .attr('class', 'svg_chart')
    }

    changeLayout () {
      this.updateSankey()
    }

    initLayout () {
      this.chart.sankey = d3Sankey.sankey()
        .nodeWidth(15)
        .nodePadding(10)
        .extent([
          [this.chart.padding.left, this.chart.padding.top],
          [this.chart.divWidth, this.chart.divHeight]
        ])

      this.chart.node = this.chart.svg.append('g')
        .attr('class', 'nodes')
        .style('font-family', 'sans-serif')
        .style('font-size', 10)

      this.chart.link = this.chart.svg.append('g')
        .attr('class', 'links')
        .style('fill', 'none')
        .style('stroke', '#000')
        .style('stroke-opacity', 0.2)
    }

    drawSankey () {
      // let drag = d3.drag()
      //   .subject(function (d) {
      //     return d
      //   })
      //   .on('start', function () {
      //     this.parentNode.appendChild(this)
      //   })
      //   .on('drag', function (d) {
      //     d3.select(this)
      //       .attr('transform', 'translate(' + 0 + ',' + d3.event.y + ')')

      //   })
      let svg = this.chart.svg
      this.chart.sankey.nodeAlign(d3Sankey[this.layout])

      let sankeData = this.chart.sankey(this.data)

      let link = this.chart.link.selectAll('.link')
        .data(sankeData.links)

      let linkEnter = link.enter()
        .append('g')
        .attr('class', 'link')
        .on('mouseover', linkMouseOver)
        .on('mouseout', mouseOut)

      linkEnter.append('path')
        .attr('d', d3Sankey.sankeyLinkHorizontal())
        .style('stroke-dasharray', function (d) {
          return this.getTotalLength()
        })
        .style('stroke-dashoffset', function (d) {
          return this.getTotalLength()
        })
        .style('stroke', 'red')
        .style('stroke-width', d => Math.max(4, d.width))

      let linkUpdate = linkEnter.merge(link)

      linkUpdate.select('path')
        .transition()
        .duration(1000)
        .style('stroke-dashoffset', 0)

      link.exit().remove()

      let node = this.chart.node.selectAll('.node')
        .data(sankeData.nodes)

      let nodeEnter = node.enter()
        .append('g')
        .attr('class', 'node')
        .on('mouseover', nodeMouseOver)
        .on('mouseout', mouseOut)
        // .call(drag)

      nodeEnter.append('rect')
        .attr('width', 0)
        .attr('height', d => (d.y1 - d.y0))
        .attr('rx', 2)
        .attr('ry', 2)
        .style('fill', (d, i) => d.color || this.color(i))
        .attr('stroke', '#000')
        .attr('x', d => d.x0)
        .attr('y', d => d.y0)

      nodeEnter.append('text')
        .attr('x', d => d.x1 > this.chart.divWidth / 2 ? (d.x0 - 5 - d.name.length * 5) : d.x1 + 5)
        .attr('y', d => (d.y1 - d.y0) / 2 + d.y0)
        .text(d => d.name)

      let nodeUpdate = nodeEnter.merge(node)

      nodeUpdate.select('rect')
        .transition()
        .duration(750)
        .attr('width', d => d.x1 - d.x0)

      nodeUpdate.select('text')
        .transition()
        .duration(750)
        .attr('x', d => d.x1 > this.chart.divWidth / 2 ? (d.x0 - 5 - d.name.length * 5) : d.x1 + 5)
        .attr('y', d => (d.y1 - d.y0) / 2 + d.y0)

      node.exit().remove()

      function nodeMouseOver (d) {
        svg.select('.nodes').selectAll('.node')
          .filter(o => {
            let flag = false
            for (let i = 0; i < d.sourceLinks.length; ++i) {
              if (o === d.sourceLinks[i].target) {
                flag = true
              }
            }
            for (let i = 0; i < d.targetLinks.length; ++i) {
              if (o === d.targetLinks[i].source) {
                flag = true
              }
            }
            if (d.index === o.index) flag = true
            if (flag === false) return o
          })
          .style('opacity', 0.2)

        svg.select('.links').selectAll('.link')
          .filter(o => {
            return (d.sourceLinks.indexOf(o) === -1) && (d.targetLinks.indexOf(o) === -1)
          })
          .style('opacity', 0.3)
      }

      function mouseOut (d) {
        svg.select('.nodes').selectAll('.node')
          .style('opacity', 1)

        svg.select('.links').selectAll('.link')
          .style('opacity', 1)
      }

      function linkMouseOver (d) {
        svg.select('.nodes').selectAll('.node')
          .filter(o => {
            return o !== d.target && o !== d.source
          })
          .style('opacity', 0.2)

        svg.select('.links').selectAll('.link')
          .filter(o => {
            return d !== o
          })
          .style('opacity', 0.3)
      }
    }

    updateSankey () {
      this.chart.sankey.nodeAlign(d3Sankey[this.layout])

      let sankeData = this.chart.sankey(this.data)

      this.chart.sankey.update(sankeData)

      this.chart.link.selectAll('.link')
        .data(sankeData.links)
        .select('path')
        .transition()
        .duration(750)
        .attr('d', d3Sankey.sankeyLinkHorizontal())

      let nodes = this.chart.node.selectAll('.node')
        .data(sankeData.nodes)

      nodes.selectAll('rect')
        .transition()
        .duration(750)
        .attr('x', d => d.x0)
        .attr('y', d => d.y0)

      nodes.selectAll('text')
        .transition()
        .duration(750)
        .attr('x', d => d.x1 > this.chart.divWidth / 2 ? (d.x0 - 5 - d.name.length * 5) : d.x1 + 5)
        .attr('y', d => (d.y1 - d.y0) / 2 + d.y0)
    }
}
